package com.isotronica.neveras.datos.daos;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Time;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

/**
 * Clase de utilidades para realizar consultas SQL. Permite una capa de
 * abstracci�n para minimizar la necesidad de escribir c�digo SQL desde las
 * aplicaciones de los usuarios.
 * 
 * @param < VO >
 *            Tipo de objetos que manipularan los datos mapeados de la BD.
 * @author Julian Alejandro Lamprea (&copy;)2009
 * @version 1.5
 */
public abstract class GenericDAO<VO> {

	/**
	 * @uml.property name="creator"
	 * @uml.associationEnd
	 */
	protected CallBackCreator<VO> creator;
	protected final Connection connection;
	

	/* String para identificar cual debe ser el ordenamiento de los resultados. */
	protected String orderBy;

	/*
	 * valor limite para realizar consultas limitadas. Indica la posicion de
	 * inicio de la entrega de resultados
	 */
	protected int start = -1;
	/*
	 * valor limite para realizar consultas limitadas. Indica la cantidad de
	 * resultados que se entregaran
	 */
	protected int limit = -1;

	protected GenericDAO(Connection connection) {
		this.connection = connection;
		
	}

	/**
	 * @return
	 * @uml.property name="creator"
	 */
	public CallBackCreator<VO> getCreator() {
		return creator;
	}

	/**
	 * @param creator
	 * @uml.property name="creator"
	 */
	public void setCreator(CallBackCreator<VO> creator) {
		this.creator = creator;
	}

	/**
	 * @param orderBy
	 * @uml.property name="orderBy"
	 */
	public void setOrderBy(String orderBy) {
		this.orderBy = orderBy;
	}

	/**
	 * @param limit
	 * @uml.property name="limit"
	 */
	public void setLimit(int limit) {
		this.limit = limit;
	}

	/**
	 * @param start
	 * @uml.property name="start"
	 */
	public void setStart(int start) {
		this.start = start;
	}

	
	public List<VO> callStorageQuery(String procedure,
			ArrayList<Object> parametros) throws SQLException {
		StringBuffer sql = new StringBuffer("{Call " + procedure + "(");

		for (int i = 0; i < parametros.size(); i++) {
			sql.append("?" + ",");
		}
		String sub = sql.substring(0, sql.length() - 1);
		sql = new StringBuffer(sub);
		sql.append(")}");

		CallableStatement cs = connection.prepareCall(sql.toString());

		fillPreparedCall(parametros, cs);
		ResultSet rs = cs.executeQuery();

		List<VO> list = new ArrayList<VO>();

		while (rs.next()) {
			list.add(creator.create(rs));
		}
		rs.close();

		return list;

	}
	
	public VO callStorageQuerySingle( String procedure, ArrayList<Object> parametros) throws SQLException{
		StringBuffer sql = new StringBuffer("{Call " + procedure + "(");
	
		VO vo = null;
		
		for (int i = 0; i < parametros.size(); i++) {
			sql.append("?" + ",");
		}
		String sub = sql.substring(0, sql.length() - 1);
		sql = new StringBuffer(sub);
		sql.append(")}");

		CallableStatement cs = connection.prepareCall(sql.toString());

		fillPreparedCall(parametros, cs);
		ResultSet rs = cs.executeQuery();

		if (rs.next()) 
			vo = creator.create(rs);
		
		rs.close();
		
		return vo;
	}

	public String callStorageInsert(String procedure,
			ArrayList<Object> parametros) throws SQLException {
		StringBuffer sql = new StringBuffer("{Call " + procedure + "(");

		for (int i = 0; i < parametros.size(); i++) {
			sql.append("?" + ",");
		}
		
		sql.append("?)}");

		CallableStatement cs = connection.prepareCall(sql.toString());
		fillPreparedCall(parametros, cs);
		cs.registerOutParameter(parametros.size() + 1, java.sql.Types.VARCHAR);

		cs.execute();

		String result = cs.getString(parametros.size() + 1);
		//System.out.println(result);
		return result;

	}
	
	public String callStorageUpdate(String procedure,
			ArrayList<Object> parametros) throws SQLException {
		StringBuffer sql = new StringBuffer("{Call " + procedure + "(");

		for (int i = 0; i < parametros.size(); i++) {
			sql.append("?" + ",");
		}
		
		sql.append("?)}");

		CallableStatement cs = connection.prepareCall(sql.toString());

		cs.registerOutParameter(parametros.size() + 1, java.sql.Types.VARCHAR);

		fillPreparedCall(parametros, cs);
		cs.execute();

		String result = cs.getString(parametros.size() + 1);
		
		return result;

	}

	private void fillPreparedCall(ArrayList<Object> data, CallableStatement cs)
			throws SQLException {

		int i = 1;

		for (Object obj : data) {
			// por cada clave extare su correspondiente valor
			if (obj instanceof Integer) {
				cs.setInt(i, (Integer) obj);
			} else if (obj instanceof String) {
				cs.setString(i, obj.toString());
			} else if (obj instanceof Time) {
				cs.setTime(i, (Time) obj);
			} else if (obj instanceof Float) {
				cs.setFloat(i, (Float) obj);
			} else if (obj instanceof Double) {
				cs.setDouble(i, (Double) obj);
			} else if (obj instanceof Date) {
				cs.setDate(i, (Date) obj);
			} else if (obj == null) {
				cs.setString(i, "");
			} else {
				throw new SQLException("Data type (" + obj.getClass().getName()
						+ ")not implemented yet.");
			}
			i++;
		}
	}

}